{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f1737a73",
   "metadata": {},
   "source": [
    "# RecursiveJsonSplitter\n",
    "\n",
    "이 JSON 분할기는 JSON 데이터를 깊이 우선 탐색(depth-first traversal)하여 더 작은 JSON 청크(chunk)를 생성합니다.\n",
    "\n",
    "이 분할기는 중첩된 JSON 객체를 가능한 한 유지하려고 시도하지만, 청크의 크기를 min_chunk_size와 max_chunk_size 사이로 유지하기 위해 필요한 경우 객체를 분할합니다. 값이 중첩된 JSON이 아니라 매우 큰 문자열인 경우, 해당 문자열은 분할되지 않습니다.\n",
    "\n",
    "청크 크기에 대한 엄격한 제한이 필요한 경우, 이 분할기 이후에 Recursive Text Splitter를 사용하여 해당 청크를 처리하는 것을 고려해 볼 수 있습니다.\n",
    "\n",
    "**분할하는 기준**\n",
    "\n",
    "1. 텍스트 분할 방식: JSON 값 기준\n",
    "2. 청크 크기 측정 방식: 문자 수 기준\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e711ebe6",
   "metadata": {},
   "source": [
    "- `requests.get()` 함수를 사용하여 \"https://api.smith.langchain.com/openapi.json\" URL에서 JSON 데이터를 가져옵니다.\n",
    "- 가져온 JSON 데이터는 `json()` 메서드를 통해 Python 딕셔너리 형태로 변환되어 `json_data` 변수에 저장됩니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "731c2456",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "\n",
    "# JSON 데이터를 로드합니다.\n",
    "json_data = requests.get(\"https://api.smith.langchain.com/openapi.json\").json()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3dc04afa",
   "metadata": {},
   "outputs": [],
   "source": [
    "json_data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb35df72",
   "metadata": {},
   "source": [
    "`RecursiveJsonSplitter`를 사용하여 JSON 데이터를 분할하는 예제입니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0cd3e80c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_text_splitters import RecursiveJsonSplitter\n",
    "\n",
    "# JSON 데이터를 최대 300 크기의 청크로 분할하는 RecursiveJsonSplitter 객체를 생성합니다.\n",
    "splitter = RecursiveJsonSplitter(max_chunk_size=300)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "632e1dea",
   "metadata": {},
   "source": [
    "`splitter.split_json()` 함수를 사용하여 JSON 데이터를 재귀적으로 분할합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96dddecc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# JSON 데이터를 재귀적으로 분할합니다. 작은 JSON 조각에 접근하거나 조작해야 하는 경우에 사용합니다.\n",
    "json_chunks = splitter.split_json(json_data=json_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5343c23",
   "metadata": {},
   "source": [
    "- `splitter.create_documents()` 메서드를 사용하여 JSON 데이터를 문서 형식으로 변환합니다.\n",
    "- `splitter.split_text()` 메서드를 사용하여 JSON 데이터를 문자열 리스트로 분할합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8075170a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# JSON 데이터를 기반으로 문서를 생성합니다.\n",
    "docs = splitter.create_documents(texts=[json_data])\n",
    "\n",
    "# JSON 데이터를 기반으로 문자열 청크를 생성합니다.\n",
    "texts = splitter.split_text(json_data=json_data)\n",
    "\n",
    "# 첫 번째 문자열을 출력합니다.\n",
    "print(docs[0].page_content)\n",
    "\n",
    "print(\"===\" * 20)\n",
    "# 분할된 문자열 청크를 출력합니다.\n",
    "print(texts[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86dbed4f",
   "metadata": {},
   "source": [
    "`texts[2]`을 출력하여 큰 청크 중 하나를 검토한 결과, 해당 청크에 리스트 객체가 포함되어 있음을 확인할 수 있습니다.\n",
    "\n",
    "- 2번째 청크의 크기가 제한(300) 을 초과하는 데에는 리스트 객체인 이유가 있습니다.\n",
    "- 이는 `RecursiveJsonSplitter` 가 **리스트 객체는 분할하지 않기 때문** 입니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1e1a61b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 청크의 크기를 확인해 봅시다.\n",
    "print([len(text) for text in texts][:10])\n",
    "\n",
    "# 더 큰 청크 중 하나를 검토해 보면 리스트 객체가 있는 것을 볼 수 있습니다.\n",
    "print(texts[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69737bf9",
   "metadata": {},
   "source": [
    "2번 index 청크를 다음과 같이 `json` 모듈을 사용하여 파싱할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35163094",
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "json_data = json.loads(texts[2])\n",
    "json_data[\"paths\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef5937ff",
   "metadata": {},
   "source": [
    "`convert_lists` 매개변수를 `True`로 설정하여 JSON 내의 리스트를 `index:item` 형태의 `key:value` 쌍으로 변환할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "037a8f71",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 다음은 JSON을 전처리하고 리스트를 인덱스:항목을 키:값 쌍으로 하는 딕셔너리로 변환합니다.\n",
    "texts = splitter.split_text(json_data=json_data, convert_lists=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf62b67d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 리스트가 딕셔너리로 변환되었고, 그 결과를 확인합니다.\n",
    "print(texts[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ec05900",
   "metadata": {},
   "source": [
    "`docs` 리스트의 특정 인덱스에 해당하는 문서를 확인할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "caa2224f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2번 문서를 확인합니다.\n",
    "docs[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "150d8504",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
